﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Sripirom.Pattern.UnitTest.Core.Domain;
using Sripirom.Pattern.UnitTest.Core.UnitOfWork;
using Sripirom.Pattern.UnitTest.Repository.Configuration.Helpers;

namespace Sripirom.Pattern.UnitTest.Repository.Configuration
{
    public class XmlUnitOfWorkRepository<T, TId> : IUnitOfWorkRepository
        where T : BaseEntity<TId>, IAggregateRoot
    {
        private IList<T> collection = new List<T>();
        private String _fileName;
        private String _rootName;
        private String _containName;
        private static Boolean pendingChanges;
        private Boolean _documentLoadedFromFile;
        FileSystemWatcher fileWatcher;
        public event EventHandler CurrentDocumentChanged;
        private XDocument _loadedDocument;
   
        public XmlUnitOfWorkRepository()
        {
            _fileName =  String.Format(@"Config\{0}Repository.config", typeof(T).Name );
            _rootName = String.Format(@"{0}Repository", typeof(T).Name);
            _containName = String.Format(@"{0}Container", typeof(T).Name);
            fileWatcher = new FileSystemWatcher();
            fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
            fileWatcher.Changed += fileWatcher_Changed;
        }
        private void fileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (_documentLoadedFromFile && !pendingChanges)
            {
                GetDocument(true);
            }
        }
        public XElement Root { get { return _loadedDocument.Element(_rootName).Element(_containName); } }

        /// <summary>
        /// Returns an open XDocument or create a new document
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public XDocument GetDocument(Boolean refresh = false)
        {
            if (refresh || _loadedDocument == null)
            {
                // we need to refactor it, but just to demonstrate how should work I will send this way;P
                if (File.Exists(_fileName))
                {
                    _loadedDocument = XDocument.Load(_fileName);
                    _documentLoadedFromFile = true;

                    if (fileWatcher.Path != Environment.CurrentDirectory)
                    {
                        fileWatcher.Path = Environment.CurrentDirectory;
                        fileWatcher.Filter = _fileName;
                        fileWatcher.EnableRaisingEvents = true;
                    }
                }
                else
                {
                    _loadedDocument = CreateNewDocument();
                    fileWatcher.EnableRaisingEvents = false;
                    _documentLoadedFromFile = false;
                }

                if (CurrentDocumentChanged != null) CurrentDocumentChanged(this, EventArgs.Empty);
            }

            return _loadedDocument;

        }

        private XDocument CreateNewDocument()
        {
            XDocument xDoc = new XDocument();
            xDoc.Add(new XElement(_rootName));
            return xDoc;
        }

        public void PersistCreationOf(IAggregateRoot entity)
        {
            var item = collection.FirstOrDefault(a => a.Equals(entity));
            if (item == null)
            {
                collection.Add((T)entity);
                XElement x = entity.CreateXElement();
                Root.Add(x);
                _loadedDocument.Save(_fileName);
            } 
            
        }

        public void PersistUpdateOf(IAggregateRoot entity)
        {
            var item = collection.FirstOrDefault(a => a.Equals(entity));
            if (item != null)
            {
                int index = collection.IndexOf(item);
                collection.Remove(item);
                collection.Insert(index, (T)entity);
                Root.Add(entity.CreateXElement());
                _loadedDocument.Save(_fileName);
            } 
        }

        public void PersistDeletionOf(IAggregateRoot entity)
        {
            var item = collection.FirstOrDefault(a => a.Equals(entity));
            if (item != null)
            {
                String id = ((T)entity).Id.ToString();
                collection.Remove(item);
                Root.Descendants(typeof(T).Name)
                .FirstOrDefault(e => e.Attribute("Id").Value == id).Remove();
                _loadedDocument.Save(_fileName);
            } 
        }

  

    }
}
